#include "libDisco.h"

static datosDisco_t datosDisco;
static pthread_rwlock_t mutexDisco = PTHREAD_RWLOCK_INITIALIZER;
static uint32_t retardoLecturaEscritura = 0;
static pthread_mutex_t mutexRetardo = PTHREAD_MUTEX_INITIALIZER;
static int32_t inotifyFd;

uint8_t inicializarDisco(char *path, uint32_t retardo) {
	if ((datosDisco.disco = open(path, O_RDWR)) == -1) {
		printf("------------------------------------------------------------\n");
		printf("--                         ERROR                          --\n");
		printf("------------------------------------------------------------\n");
		printf("El archivo correspondiente al disco no pudo ser abierto.\n");
		printf("Compruebe que se encuentre en [%s].\n", path);
		printf("------------------------------------------------------------\n");
		printf("\n");
		return 1;
	}
	datosDisco.tamanio = lseek(datosDisco.disco, 0, SEEK_END);
	printfCUCA("tamaño: %d\n", datosDisco.tamanio);
	lseek(datosDisco.disco, 0, SEEK_SET);
	printfCUCA("intento mapear a memoria el disco\n");
	if ((datosDisco.ptrDisco = mmap(NULL, datosDisco.tamanio, (PROT_READ | PROT_WRITE), MAP_SHARED, datosDisco.disco, 0)) == MAP_FAILED) {
		printf("------------------------------------------------------------\n");
		printf("--                         ERROR                          --\n");
		printf("------------------------------------------------------------\n");
		printf("Error en el mmap.\n");
		perror("Error:");
		printf("------------------------------------------------------------\n");
		printf("\n");
		return 2;
	}
	retardoLecturaEscritura = retardo;
/*
	//INOTIFY
	inotifyFd = inotify_init();
	if (inotifyFd == -1) {
		printfCUCA("Error en la inicializacion de inotify.\n");
		return 3;
	}
	int32_t wd = inotify_add_watch(inotifyFd, PATH_DIRECTORIO_ARCHIVO_CONFIGURACION, IN_ALL_EVENTS);
	if (wd == -1) {
		printfCUCA("Error al agregar el archivo de configuracion a la lista de observacion de inotify.\n");
		return 4;
	}
	pthread_t thread_id = 0;
	pthread_create(&thread_id, NULL, (void*) &monitorearRetardo, NULL);
*/
	/*TODO HABILITAR ESTO PARA QUE SE ESCRIBA EL DISCO CADA X TIEMPO
	 pthread_t thread_id = 0;
	 pthread_create(&thread_id, NULL, (void*) &threadSincronizacionDisco, NULL);*/
	return 0;
}

static uint8_t analizarEvento(struct inotify_event *i) {
	/*printfCUCA("    wd =%2d; ", i->wd);
	 if (i->cookie > 0)
	 printfCUCA("cookie =%4d; ", i->cookie);

	 printfCUCA("mask = ");
	 if (i->mask & IN_ACCESS)
	 printfCUCA("IN_ACCESS ");
	 if (i->mask & IN_ATTRIB)
	 printfCUCA("IN_ATTRIB ");
	 if (i->mask & IN_CLOSE_NOWRITE)
	 printfCUCA("IN_CLOSE_NOWRITE ");
	 if (i->mask & IN_CLOSE_WRITE)
	 printfCUCA("IN_CLOSE_WRITE ");
	 if (i->mask & IN_CREATE)
	 printfCUCA("IN_CREATE ");
	 if (i->mask & IN_DELETE)
	 printfCUCA("IN_DELETE ");
	 if (i->mask & IN_DELETE_SELF)
	 printfCUCA("IN_DELETE_SELF ");
	 if (i->mask & IN_IGNORED)
	 printfCUCA("IN_IGNORED ");
	 if (i->mask & IN_ISDIR)
	 printfCUCA("IN_ISDIR ");
	 if (i->mask & IN_MODIFY)
	 printfCUCA("IN_MODIFY ");
	 if (i->mask & IN_MOVE_SELF)
	 printfCUCA("IN_MOVE_SELF ");
	 if (i->mask & IN_MOVED_FROM)
	 printfCUCA("IN_MOVED_FROM ");
	 if (i->mask & IN_MOVED_TO)
	 printfCUCA("IN_MOVED_TO ");
	 if (i->mask & IN_OPEN)
	 printfCUCA("IN_OPEN ");
	 if (i->mask & IN_Q_OVERFLOW)
	 printfCUCA("IN_Q_OVERFLOW ");
	 if (i->mask & IN_UNMOUNT)
	 printfCUCA("IN_UNMOUNT ");
	 printfCUCA("\n");*/

	uint8_t retorno = 0;
/*
	if (i->len > 0) {
		printfCUCA("        name = %s\n", i->name);
		if (strcmp(i->name, NOMBRE_ARCHIVO_CONFIGURACION) == 0 && (i->mask & IN_CLOSE_WRITE)) {
			retorno = 1;
		}
	}*/
	return retorno;
}

void monitorearRetardo(void) {
	while (1) {
		char buf[BUF_LEN];
		ssize_t numRead = read(inotifyFd, buf, BUF_LEN);
		printfCUCA("Se leyeron %d bytes.\n", numRead);
		char *p;
		struct inotify_event *event;
		uint8_t actualizar = 0;
		for (p = buf; p < buf + numRead;) {
			event = (struct inotify_event *) p;
			uint8_t actualizarAux = analizarEvento(event);
			if (!actualizar) {
				actualizar = actualizarAux;
			}
			p += sizeof(struct inotify_event) + event->len;
		}/*
		if (actualizar) {
			archConfig_t archConfig;
			leerArchivoConfiguracion(&archConfig, 1);
			pthread_mutex_lock(&mutexRetardo);
			retardoLecturaEscritura = archConfig.retardo;
			pthread_mutex_unlock(&mutexRetardo);
		}*/
	}
}

char* leerBytes(off_t offset, size_t size) {
	pthread_mutex_lock(&mutexRetardo);
	uint32_t retardoAux = retardoLecturaEscritura;
	pthread_mutex_unlock(&mutexRetardo);
	printfCUCA("Se usara un retardo de lectura de %d.\n", retardoAux);
	usleep(retardoAux);
	char* lectura = malloc(size);
	pthread_rwlock_rdlock(&mutexDisco); //TODO SACARLO
	memcpy(lectura, datosDisco.ptrDisco + offset, size);
	pthread_rwlock_unlock(&mutexDisco); //TODO SACARLO
	return lectura;
}

void escribirBytes(off_t offset, size_t size, char* info) {
	pthread_mutex_lock(&mutexRetardo);
	uint32_t retardoAux = retardoLecturaEscritura;
	pthread_mutex_unlock(&mutexRetardo);
	printfCUCA("Se usara un retardo de escritura de %d.\n", retardoAux);
	usleep(retardoAux);
	pthread_rwlock_rdlock(&mutexDisco); //TODO SACARLO
	memcpy(datosDisco.ptrDisco + offset, info, size);
	pthread_rwlock_unlock(&mutexDisco); //TODO SACARLO
}

void threadSincronizacionDisco(void) {
	while (1) {
		sleep(600);
		pthread_rwlock_wrlock(&mutexDisco);
		sincronizarDisco();
		pthread_rwlock_unlock(&mutexDisco);
	}
}

void sincronizarDisco(void) {
	msync(datosDisco.ptrDisco, datosDisco.tamanio, MS_SYNC);
}

void finalizarDisco(void) {
	pthread_rwlock_wrlock(&mutexDisco);
	sincronizarDisco();
	if (munmap(datosDisco.ptrDisco, datosDisco.tamanio) == -1) {
		printfCUCA("error en el munmap. \n");
	}
	close(datosDisco.disco);
	printfCUCA("finalize el disco.\n");
	pthread_rwlock_unlock(&mutexDisco);
}
